home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / msdos / lynx / source / www / library / implemen / htftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  50.7 KB  |  1,814 lines

  1. /*            File Transfer Protocol (FTP) Client
  2. **            for a WorldWideWeb browser
  3. **            ===================================
  4. **
  5. **    A cache of control connections is kept.
  6. **
  7. ** Note: Port allocation
  8. **
  9. **    It is essential that the port is allocated by the system, rather
  10. **    than chosen in rotation by us (POLL_PORTS), or the following
  11. **    problem occurs.
  12. **
  13. **    It seems that an attempt by the server to connect to a port which has
  14. **    been used recently by a listen on the same socket, or by another
  15. **    socket this or another process causes a hangup of (almost exactly)
  16. **    one minute. Therefore, we have to use a rotating port number.
  17. **    The problem remains that if the application is run twice in quick
  18. **    succession, it will hang for what remains of a minute.
  19. **
  20. ** Authors
  21. **    TBL    Tim Berners-lee <timbl@info.cern.ch>
  22. **    DD    Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
  23. **      LM      Lou Montulli <montulli@ukanaix.cc.ukans.edu>
  24. **      FM      Foteos Macrides <macrides@sci.wfeb.edu>
  25. ** History:
  26. **     2 May 91    Written TBL, as a part of the WorldWideWeb project.
  27. **    15 Jan 92    Bug fix: close() was used for NETCLOSE for control soc
  28. **    10 Feb 92    Retry if cached connection times out or breaks
  29. **     8 Dec 92    Bug fix 921208 TBL after DD
  30. **    17 Dec 92    Anon FTP password now just WWWuser@ suggested by DD
  31. **            fails on princeton.edu!
  32. **    27 Dec 93 (FM)  Fixed up so FTP now works with VMS hosts.  Path
  33. **            must be Unix-style and cannot include the device
  34. **            or top directory.
  35. **      ?? ??? ?? (LM)  Added code to prompt and send passwords for non
  36. **            anonymous FTP
  37. **      25 Mar 94 (LM)  Added code to recognize different ftp server types
  38. **                      and code to parse dates and sizes on most hosts.
  39. **    27 Mar 93 (FM)  Added code for getting dates and sizes on VMS hosts.
  40. **
  41. ** Options:
  42. **    LISTEN        We listen, the other guy connects for data.
  43. **            Otherwise, other way round, but problem finding our
  44. **            internet address!
  45. **
  46. ** Notes:
  47. **                 Portions Copyright 1994 Trustees of Dartmouth College
  48. **             Code for recognizing different FTP servers and
  49. **            parsing "ls -l" output taken from Macintosh Fetch
  50. **            program with permission from Jim Matthews,
  51. **            Dartmouth Software Development Team.
  52. */
  53.  
  54. /* #define LISTEN     Not for msdos */
  55.  
  56. /*
  57. BUGS:    @@@      Limit connection cache size!
  58.         Error reporting to user.
  59.         400 & 500 errors are acked by user with windows.
  60.         Use configuration file for user names
  61.  
  62. **        Note for portablility this version does not use select() and
  63. **        so does not watch the control and data channels at the
  64. **        same time.
  65. */
  66.  
  67. #include"capstdio.h"
  68. #include"capalloc.h"
  69.  
  70. #include "HTFTP.h"    /* Implemented here */
  71.  
  72. /* this define should be in HTFont.h :( */
  73. #define HT_NON_BREAK_SPACE ((char)1)   /* For now */
  74.  
  75. #define REPEAT_PORT    /* Give the port number for each file */
  76. #define REPEAT_LISTEN    /* Close each listen socket and open a new one */
  77.  
  78. /* define POLL_PORTS         If allocation does not work, poll ourselves.*/
  79. #define LISTEN_BACKLOG 2    /* Number of pending connect requests (TCP)*/
  80.  
  81. #define FIRST_TCP_PORT    1024    /* Region to try for a listening port */
  82. #define LAST_TCP_PORT    5999    
  83.  
  84. #define LINE_LENGTH 256
  85. #define COMMAND_LENGTH 256
  86.  
  87. #include "HTParse.h"
  88. #include "HTUtils.h"
  89. #include "tcp.h"
  90. #include "HTTCP.h"
  91. #include "HTAnchor.h"
  92. #include "HTFile.h"    /* For HTFileFormat() */
  93. #include "HTBTree.h"
  94. #include "HTChunk.h"
  95. #include "HTAlert.h"
  96. #ifndef IPPORT_FTP
  97. #define IPPORT_FTP    21
  98. #endif
  99.  
  100. #ifdef REMOVED_CODE
  101. extern char *malloc();
  102. extern void free();
  103. extern char *strncpy();
  104. #endif
  105.  
  106. /*
  107.  *    MSDos has no such define
  108.  */
  109. typedef unsigned long int u_long;
  110.  
  111. typedef struct _connection {
  112.     struct _connection *    next;    /* Link on list     */
  113.     u_long            addr;    /* IP address        */
  114.     int                socket;    /* Socket number for communication */
  115.     BOOL            binary; /* Binary mode? */
  116. } connection;
  117.  
  118. #ifndef NIL
  119. #define NIL 0
  120. #endif
  121.  
  122. /*        Hypertext object building machinery
  123. */
  124. #include "HTML.h"
  125.  
  126. /*
  127.  *    Some dos globals for screwy argument passing.
  128.  */
  129. extern void *vp_msdosmem;
  130. extern void **vpp_msdosmem;
  131.  
  132. #define PUTC(c) (*targetClass.put_character)(target, c)
  133. #define PUTS(s) (*targetClass.put_string)(target, s)
  134. #define START(e) (vp_msdosmem = NULL, vpp_msdosmem = NULL, (*targetClass.start_element)(target, e, 0, 0))
  135.  
  136. #define END(e) (*targetClass.end_element)(target, e)
  137. #define FREE_TARGET (*targetClass.free)(target)
  138. #define ABORT_TARGET (*targetClass.free)(target)
  139. struct _HTStructured {
  140.     CONST HTStructuredClass *    isa;
  141.     /* ... */
  142. };
  143.  
  144.  
  145. /*    Global Variables
  146. **    ---------------------
  147. */
  148. PUBLIC BOOLEAN HTfileSortMethod = FILE_BY_NAME;
  149.  
  150. /*    Module-Wide Variables
  151. **    ---------------------
  152. */
  153. PRIVATE connection * connections =0;    /* Linked list of connections */
  154. PRIVATE char    response_text[LINE_LENGTH+1];/* Last response from NewsHost */
  155. PRIVATE connection * control;        /* Current connection */
  156. PRIVATE int    data_soc = -1;        /* Socket for data transfer =invalid */
  157.  
  158. #define GENERIC_SERVER    0
  159. #define MACHTEN_SERVER     1
  160. #define UNIX_SERVER     2
  161. #define VMS_SERVER     3
  162. #define CMS_SERVER     4
  163. #define DCTS_SERVER        5
  164. #define TCPC_SERVER    6
  165. #define PETER_LEWIS_SERVER    7
  166. #define NCSA_SERVER    8
  167.  
  168. PRIVATE int     server_type = GENERIC_SERVER;   /* the type of ftp host */
  169. PRIVATE int     unsure_type = FALSE;            /* sure about the type? */
  170. PRIVATE BOOLEAN use_list = FALSE;        /* use the LIST command? */
  171.  
  172. #ifdef POLL_PORTS
  173. PRIVATE    unsigned short    port_number = FIRST_TCP_PORT;
  174. #endif
  175.  
  176. #ifdef LISTEN
  177. PRIVATE int     master_socket = -1;    /* Listening socket = invalid    */
  178. PRIVATE char    port_command[255];    /* Command for setting the port */
  179. PRIVATE fd_set    open_sockets;         /* Mask of active channels */
  180. PRIVATE int    num_sockets;          /* Number of sockets to scan */
  181. #else
  182. PRIVATE    unsigned short    passive_port;    /* Port server specified for data */
  183. #endif
  184.  
  185.  
  186. #define NEXT_CHAR HTGetChararcter()    /* Use function in HTFormat.c */
  187.  
  188. #define DATA_BUFFER_SIZE 2048
  189. PRIVATE char data_buffer[DATA_BUFFER_SIZE];        /* Input data buffer */
  190. PRIVATE char * data_read_pointer;
  191. PRIVATE char * data_write_pointer;
  192. #define NEXT_DATA_CHAR next_data_char()
  193.  
  194.  
  195. /*    Procedure: Read a character from the data connection
  196. **    ----------------------------------------------------
  197. */
  198. PRIVATE char next_data_char
  199. NOARGS
  200. {
  201.     int status;
  202.     if (data_read_pointer >= data_write_pointer) {
  203.     status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
  204.       if (status <= 0)
  205.     return (char)-1;
  206.       data_write_pointer = data_buffer + status;
  207.       data_read_pointer = data_buffer;
  208.     }
  209. #ifdef NOT_ASCII
  210.     {
  211.     char c = *data_read_pointer++;
  212.     return FROMASCII(c);
  213.     }
  214. #else
  215.     return *data_read_pointer++;
  216. #endif
  217. }
  218.  
  219.  
  220. /*    Close an individual connection
  221. **
  222. */
  223. static int close_connection(connection *con);
  224. int close_connection(connection *con)
  225. {
  226.     connection * scan;
  227.     int status = NETCLOSE(con->socket);
  228. #ifndef RELEASE
  229.     if (TRACE) fprintf(stderr, "FTP: Closing control socket %d\n", con->socket);
  230. #endif /* RELEASE */
  231.     if (connections==con) {
  232.         connections = con->next;
  233.     return status;
  234.     }
  235.     for(scan=connections; scan; scan=scan->next) {
  236.         if (scan->next == con) {
  237.         scan->next = con->next;    /* Unlink */
  238.         if (control==con) control = (connection*)0;
  239.         return status;
  240.     } /*if */
  241.     } /* for */
  242.     return -1;        /* very strange -- was not on list. */
  243. }
  244.  
  245. PRIVATE char *help_message_buffer = 0;  /* global :( */
  246.  
  247. PRIVATE void init_help_message_cache NOARGS
  248. {
  249.     if(help_message_buffer) free(help_message_buffer);
  250.     help_message_buffer = 0;
  251. }
  252.  
  253. PRIVATE void help_message_cache_add ARGS1(char *,string)
  254. {
  255.     if(help_message_buffer)
  256.         StrAllocCat(help_message_buffer, string);
  257.     else    
  258.         StrAllocCopy(help_message_buffer, string);
  259.  
  260. #ifndef RELEASE
  261.     if(TRACE)
  262.     fprintf(stderr,"Adding message to help cache: %s\n",string);
  263. #endif /* RELEASE */
  264. }
  265.  
  266. PRIVATE char *help_message_cache_non_empty NOARGS
  267. {
  268.   return(help_message_buffer);
  269. }
  270. PRIVATE char *help_message_cache_contents NOARGS
  271. {
  272.    return(help_message_buffer);
  273. }
  274.  
  275. /*    Execute Command and get Response
  276. **    --------------------------------
  277. **
  278. **    See the state machine illustrated in RFC959, p57. This implements
  279. **    one command/reply sequence.  It also interprets lines which are to
  280. **    be continued, which are marked with a "-" immediately after the
  281. **    status code.
  282. **
  283. **    Continuation then goes on until a line with a matching reply code
  284. **    an a space after it.
  285. **
  286. ** On entry,
  287. **    con    points to the connection which is established.
  288. **    cmd    points to a command, or is NIL to just get the response.
  289. **
  290. **    The command is terminated with the CRLF pair.
  291. **
  292. ** On exit,
  293. **    returns:  The first digit of the reply type,
  294. **          or negative for communication failure.
  295. */
  296. static int response(char *cmd);
  297. int response(char *cmd)
  298. {
  299.     int result;                /* Three-digit decimal code */
  300.     int    continuation_response = -1;
  301.     int status;
  302.  
  303.     if (!control) {
  304. #ifndef RELEASE
  305.       if(TRACE) fprintf(stderr, "FTP: No control connection set up!!\n");
  306. #endif /* RELEASE */
  307.       return -99;
  308.     }
  309.  
  310.     if (cmd) {
  311.  
  312. #ifndef RELEASE
  313.     if (TRACE) fprintf(stderr, "  Tx: %s", cmd);
  314. #endif /* RELEASE */
  315.  
  316. #ifdef NOT_ASCII
  317.     {
  318.         char * p;
  319.         for(p=cmd; *p; p++) {
  320.             *p = TOASCII(*p);
  321.         }
  322.     }
  323. #endif 
  324.     status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
  325.     if (status<0) {
  326. #ifndef RELEASE
  327.         if (TRACE) fprintf(stderr,
  328.         "FTP: Error %d sending command: closing socket %d\n",
  329.         status, control->socket);
  330. #endif /* RELEASE */
  331.         close_connection(control);
  332.         return status;
  333.     }
  334.     }
  335.  
  336.     do {
  337.     char *p = response_text;
  338.     for(;;) {  
  339.         if (((*p++=NEXT_CHAR) == LF)
  340.             || (p == &response_text[LINE_LENGTH])) {
  341.  
  342.         char continuation;
  343.  
  344.  
  345. /*        *p++=0;             Terminate the string */
  346.         *p = '\0';
  347. #ifndef RELEASE
  348.         if (TRACE) fprintf(stderr, "    Rx: %s", response_text);
  349. #endif /* RELEASE */
  350.  
  351.         if(server_type == UNIX_SERVER && 
  352.                     !strncmp(response_text,"250-",4))
  353.             help_message_cache_add(response_text+4);
  354.  
  355.         sscanf(response_text, "%d%c", &result, &continuation);
  356.         if  (continuation_response == -1) {
  357.             if (continuation == '-')  /* start continuation */
  358.                 continuation_response = result;
  359.         } else {     /* continuing */
  360.             if (continuation_response == result
  361.                 && continuation == ' ')
  362.                 continuation_response = -1;    /* ended */
  363.         }
  364.         break;
  365.         } /* if end of line */
  366.  
  367.  
  368.         if (*(p-1) == (char) EOF) {
  369. #ifndef RELEASE
  370.         if(TRACE) fprintf(stderr, "Error on rx: closing socket %d\n",
  371.             control->socket);
  372. #endif /* RELEASE */
  373.         strcpy(response_text, "000 *** TCP read error on response\n");
  374.         close_connection(control);
  375.         return -1;    /* End of file on response */
  376.         }
  377.     } /* Loop over characters */
  378.  
  379.     } while (continuation_response != -1);
  380.  
  381.     if (result==421) {
  382. #ifndef RELEASE
  383.     if(TRACE) fprintf(stderr, "FTP: They close so we close socket %d\n",
  384.         control->socket);
  385. #endif /* RELEASE */
  386.     close_connection(control);
  387.     return -1;
  388.     }
  389.     return result/100;
  390. }
  391.  
  392. /* this function should try to set the macintosh server into binary mode
  393.  */
  394. PRIVATE int set_mac_binary NOARGS
  395. {
  396.     /* try to set mac binary mode */
  397.     return(2 == response("MACB\r\n"));
  398. }
  399.  
  400. /* This function gets the current working directory to help
  401.  * determine what kind of host it is
  402.  */
  403.  
  404. PRIVATE void get_ftp_pwd ARGS2(int *,server_type, BOOLEAN *,use_list) {
  405.  
  406.     char *cp;
  407.     /* get the working directory (to see what it looks like) */
  408.     int status = response("PWD\r\n");
  409.     if (status < 0)
  410.         return;
  411.     else 
  412.      {
  413.  
  414.     cp = strchr(response_text+5,'"');
  415.     if(cp) *cp = '\0';
  416.         if (*server_type == TCPC_SERVER)
  417.          {
  418.             *server_type = response_text[5] == '/' ? NCSA_SERVER : TCPC_SERVER;
  419.          }
  420.         else if (response_text[5] == '/')
  421.          {
  422.             /* path names beginning with / imply Unix,
  423.          * right? 
  424.          */
  425.          if(set_mac_binary())
  426.            *server_type = NCSA_SERVER;
  427.          else
  428.         {
  429.                    *server_type = UNIX_SERVER;
  430.                    *use_list = TRUE;
  431.         }
  432.          return;
  433.          }
  434.         else if (response_text[strlen(response_text)-1] == ']')
  435.          {
  436.              /* path names ending with ] imply VMS, right? */
  437.              *server_type = VMS_SERVER;
  438.          *use_list = TRUE;
  439.          }
  440.         else
  441.              *server_type = GENERIC_SERVER;
  442.  
  443.         if ((*server_type == NCSA_SERVER) ||
  444.                (*server_type == TCPC_SERVER) ||
  445.                     (*server_type == PETER_LEWIS_SERVER))
  446.             set_mac_binary();
  447.      }
  448. }
  449.  
  450. /*    Get a valid connection to the host
  451. **    ----------------------------------
  452. **
  453. ** On entry,
  454. **    arg    points to the name of the host in a hypertext address
  455. ** On exit,
  456. **    returns    <0 if error
  457. **        socket number if success
  458. **
  459. **    This routine takes care of managing timed-out connections, and
  460. **    limiting the number of connections in use at any one time.
  461. **
  462. **    It ensures that all connections are logged in if they exist.
  463. **    It ensures they have the port number transferred.
  464. */
  465. PRIVATE int get_connection ARGS1 (CONST char *,arg)
  466. {
  467.     int status;
  468.     char * command;
  469.     connection * con = (connection *)malloc(sizeof(*con));
  470.  
  471.     char * username=0;
  472.     char * password=0;
  473.     static char *user_entered_password=0;
  474.     static char *last_username_and_host=0;
  475.     
  476.     if (!arg) return -1;        /* Bad if no name sepcified    */
  477.     if (!*arg) return -1;        /* Bad if name had zero length    */
  478.  
  479. /* Get node name:
  480. */
  481.     {
  482.     char *p1 = HTParse(arg, "", PARSE_HOST);
  483.     char *p2 = strrchr(p1, '@');    /* user? */
  484.     char * pw=0;
  485.  
  486.     if (p2!=NULL) {
  487.         username = p1;
  488.         *p2=0;            /* terminate */
  489.         p1 = p2+1;            /* point to host */
  490.         pw = strchr(username, ':');
  491.         if (pw) {
  492.             *pw++ = 0;
  493.         password = pw;
  494.         }
  495.  
  496.         /* if the password doesn't exist then we are going to have
  497.          * to ask the user for it.  The only problem is that we
  498.          * don't want to ask for it every time, so we will store
  499.          * away in a primitive fashion.
  500.          */
  501.         if(!password) {
  502.         char tmp[256];
  503.  
  504.         sprintf(tmp,"%s@%s",username,p1);
  505.         /* if the user@host is not equal to the last time through
  506.          * or user_entered_password has no data then we need
  507.          * to ask the user for the password
  508.          */
  509.         if(!last_username_and_host ||
  510.             strcmp(tmp,last_username_and_host) ||
  511.                         !user_entered_password) {
  512.  
  513.             StrAllocCopy(last_username_and_host,tmp);
  514.             sprintf(tmp,"Enter password for user %s@%s:",username,p1);
  515.             if(user_entered_password) 
  516.             free(user_entered_password);
  517.             user_entered_password = (char *)HTPromptPassword(tmp);
  518.  
  519.         } /* else we already know the password */
  520.         password = user_entered_password;
  521.         }
  522.     }
  523.  
  524.         if (!username) free(p1);
  525.     } /* scope of p1 */
  526.  
  527.         
  528.   con->socket = -1;
  529.  
  530.   status = HTDoConnect (arg, "FTP", IPPORT_FTP, &con->socket);
  531.  
  532.    
  533.   if (status < 0)
  534.     {
  535. #ifndef RELEASE
  536.       if (TRACE)
  537.     {
  538.         fprintf(stderr,
  539.             "FTP: Unable to connect to remote host for `%s'.\n",
  540.             arg);
  541.     }
  542. #endif /* RELEASE */
  543.       if (con->socket != -1)
  544.     {
  545.           NETCLOSE(con->socket);
  546.         }
  547.     
  548.       if (username)
  549.           free(username);
  550.       free(con);
  551.       return status;                    /* Bad return */
  552.     }
  553.  
  554.     
  555. #ifndef RELEASE
  556.     if (TRACE)
  557.     fprintf(stderr, "FTP connected, socket %d\n", con->socket);
  558. #endif /* RELEASE */
  559.     control = con;        /* Current control connection */
  560.  
  561.     /* Initialise buffering for contron connection */
  562.     HTInitInput(control->socket);
  563.  
  564.  
  565. /*    Now we log in        Look up username, prompt for pw.
  566. */
  567.   {
  568.     int status = response((char *)0);    /* Get greeting */
  569.  
  570.     if (status == 2) {        /* Send username */
  571.     if (username) {
  572.         command = (char*)malloc(10+strlen(username)+2+1);
  573.         if (command == NULL) outofmem(__FILE__, "get_connection");
  574.         sprintf(command, "USER %s%c%c", username, CR, LF);
  575.     } else {
  576.         command = (char*)malloc(24);
  577.         if (command == NULL) outofmem(__FILE__, "get_connection");
  578.         sprintf(command, "USER anonymous%c%c", CR, LF);
  579.         }
  580.     status = response(command);
  581.     free(command);
  582.     }
  583.     if (status == 3) {        /* Send password */
  584.     if (password) {
  585.         command = (char*)malloc(10+strlen(password)+2+1);
  586.         if (command == NULL) outofmem(__FILE__, "get_connection");
  587.         sprintf(command, "PASS %s%c%c", password, CR, LF);
  588.     } else {
  589.         char * user = getenv("USER");
  590.         CONST char *host = HTHostName();
  591.         if (!user) user = "WWWuser";
  592.         /* If not fully qualified, suppress it as ftp.uu.net
  593.            prefers a blank to a bad name */
  594.         if (!strchr(host, '.')) host = "";
  595.  
  596.         command = (char*)malloc(20+strlen(host)+2+1);
  597.         if (command == NULL) outofmem(__FILE__, "get_connection");
  598.         sprintf(command, "PASS %s@%s%c%c", user ? user : "WWWuser",
  599.                                 host, CR, LF); /*@@*/
  600.         }
  601.     status = response(command);
  602.     free(command);
  603.     }
  604.     if (username) free(username);
  605.  
  606.     if (status == 3) {
  607.         char temp[80];
  608.     sprintf(temp, "ACCT noaccount%c%c", CR, LF);
  609.     status = response(temp);
  610.     }
  611.     if (status !=2) {
  612. #ifndef RELEASE
  613.     if (TRACE) fprintf(stderr, "FTP: Login fail: %s", response_text);
  614. #endif /* RELEASE */
  615.         /* if (control->socket > 0) close_connection(control->socket); */
  616.         return -1;        /* Bad return */
  617.     }
  618. #ifndef RELEASE
  619.     if (TRACE) fprintf(stderr, "FTP: Logged in.\n");
  620. #endif /* RELEASE */
  621.     /** Check for host type **/
  622.     server_type = GENERIC_SERVER;    /* reset */
  623.     use_list = FALSE;             /* reset */
  624.     if ((status=response("SYST\r\n")) == 2) {
  625.                 /* we got a line -- what kind of server are we talking to? */
  626.          if (strncmp(response_text+4, "UNIX Type: L8 MAC-OS MachTen", 28) == 0)
  627.           {
  628.              server_type = MACHTEN_SERVER;
  629.          use_list = TRUE;
  630.           }
  631.          else if (strstr(response_text+4, "UNIX") != NULL)
  632.           {
  633.              server_type = UNIX_SERVER;
  634.          use_list = TRUE;
  635.           }
  636.          else if (strncmp(response_text+4, "VMS", 3) == 0)
  637.       {
  638.              server_type = VMS_SERVER;
  639.          use_list = TRUE;
  640.       }
  641.          else if ((strncmp(response_text+4, "VM/CMS", 6) == 0)
  642.                  || (strncmp(response_text+4, "VM ", 3) == 0))
  643.              server_type = CMS_SERVER;
  644.          else if (strncmp(response_text+4, "DCTS", 4) == 0)
  645.              server_type = DCTS_SERVER;
  646.          else if (strstr(response_text+4, "MAC-OS TCP/Connect II") != NULL)
  647.           {
  648.              server_type = TCPC_SERVER;
  649.              get_ftp_pwd(&server_type, &use_list);
  650.          unsure_type = TRUE;   
  651.           }
  652.          else if (strncmp(response_text+4, "MACOS Peter's Server", 20) == 0)
  653.           {
  654.              server_type = PETER_LEWIS_SERVER;
  655.              use_list = TRUE;
  656.              set_mac_binary();
  657.           }
  658.      else 
  659.       {
  660.          server_type = GENERIC_SERVER;
  661.              get_ftp_pwd(&server_type, &use_list);
  662.          unsure_type = TRUE;   
  663.       }
  664.     } else {
  665.     /* SYST fails :(  try to get the type from the PWD command */
  666.          get_ftp_pwd(&server_type, &use_list);
  667.     }
  668.  
  669. /*    Now we inform the server of the port number we will listen on
  670. */
  671. #ifdef NOTREPEAT_PORT
  672.     {
  673.         int status = response(port_command);
  674.         if (status !=2) {
  675.             if (control->socket) close_connection(control->socket);
  676.             return -status;        /* Bad return */
  677.         }
  678. #ifndef RELEASE
  679.         if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
  680. #endif /* RELEASE */
  681.     }
  682. #endif
  683.     return con->socket;            /* Good return */
  684.   } /* Scope of con */
  685. }
  686.  
  687.  
  688. #ifdef LISTEN
  689.  
  690. /*    Close Master (listening) socket
  691. **    -------------------------------
  692. **
  693. **
  694. */
  695. #ifdef __STDC__
  696. PRIVATE int close_master_socket(void)
  697. #else
  698. PRIVATE int close_master_socket()
  699. #endif
  700. {
  701.     int status;
  702.     FD_CLR(master_socket, &open_sockets);
  703.     status = NETCLOSE(master_socket);
  704. #ifndef RELEASE
  705.     if (TRACE) fprintf(stderr, "FTP: Closed master socket %d\n", master_socket);
  706. #endif /* RELEASE */
  707.     master_socket = -1;
  708.     if (status<0) return HTInetStatus("close master socket");
  709.     else return status;
  710. }
  711.  
  712.  
  713. /*    Open a master socket for listening on
  714. **    -------------------------------------
  715. **
  716. **    When data is transferred, we open a port, and wait for the server to
  717. **    connect with the data.
  718. **
  719. ** On entry,
  720. **    master_socket    Must be negative if not set up already.
  721. ** On exit,
  722. **    Returns        socket number if good
  723. **            less than zero if error.
  724. **    master_socket    is socket number if good, else negative.
  725. **    port_number    is valid if good.
  726. */
  727. #ifdef __STDC__
  728. PRIVATE int get_listen_socket(void)
  729. #else
  730. PRIVATE int get_listen_socket()
  731. #endif
  732. {
  733.     struct sockaddr_in soc_address;    /* Binary network address */
  734.     struct sockaddr_in* sin = &soc_address;
  735.     int new_socket;            /* Will be master_socket */
  736.     
  737.     
  738.     FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
  739.     num_sockets = 0;
  740.     
  741. #ifndef REPEAT_LISTEN
  742.     if (master_socket>=0) return master_socket;  /* Done already */
  743. #endif
  744.  
  745. /*  Create internet socket
  746. */
  747.     new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  748.     
  749.     if (new_socket<0)
  750.     return HTInetStatus("socket for master socket");
  751.     
  752. #ifndef RELEASE
  753.     if (TRACE) fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
  754. #endif /* RELEASE */
  755.     
  756. /*  Search for a free port.
  757. */
  758.     sin->sin_family = AF_INET;        /* Family = internet, host order  */
  759.     sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
  760. #ifdef POLL_PORTS
  761.     {
  762.         unsigned short old_port_number = port_number;
  763.     for(port_number=old_port_number+1;;port_number++){ 
  764.         int status;
  765.         if (port_number > LAST_TCP_PORT)
  766.         port_number = FIRST_TCP_PORT;
  767.         if (port_number == old_port_number) {
  768.         return HTInetStatus("bind");
  769.         }
  770.         soc_address.sin_port = htons(port_number);
  771.         if ((status=bind(new_socket,
  772.             (struct sockaddr*)&soc_address,
  773.                 /* Cast to generic sockaddr */
  774.             sizeof(soc_address))) == 0)
  775.         break;
  776. #ifndef RELEASE
  777.         if (TRACE) fprintf(stderr,
  778.         "TCP bind attempt to port %d yields %d, errno=%d\n",
  779.         port_number, status, SOCKET_ERRNO);
  780. #endif /* RELEASE */
  781.     } /* for */
  782.     }
  783. #else
  784.     {
  785.         int status;
  786.     int address_length = sizeof(soc_address);
  787.     status = getsockname(control->socket,
  788.             (struct sockaddr *)&soc_address,
  789.              &address_length);
  790.     if (status<0) return HTInetStatus("getsockname");
  791. #ifndef RELEASE
  792.     CTRACE(tfp, "FTP: This host is %s\n",
  793.         HTInetString(sin));
  794. #endif /* RELEASE */
  795.  
  796.     soc_address.sin_port = 0;    /* Unspecified: please allocate */
  797.     status=bind(new_socket,
  798.         (struct sockaddr*)&soc_address,
  799.             /* Cast to generic sockaddr */
  800.         sizeof(soc_address));
  801.     if (status<0) return HTInetStatus("bind");
  802.     
  803.     address_length = sizeof(soc_address);
  804.     status = getsockname(new_socket,
  805.             (struct sockaddr*)&soc_address,
  806.             &address_length);
  807.     if (status<0) return HTInetStatus("getsockname");
  808.     }
  809. #endif    
  810.  
  811. #ifndef RELEASE
  812.     CTRACE(tfp, "FTP: bound to port %d on %s\n",
  813.         (int)ntohs(sin->sin_port),
  814.         HTInetString(sin));
  815. #endif /* RELEASE */
  816.  
  817. #ifdef REPEAT_LISTEN
  818.     if (master_socket>=0)
  819.         (void) close_master_socket();
  820. #endif    
  821.     
  822.     master_socket = new_socket;
  823.     
  824. /*    Now we must find out who we are to tell the other guy
  825. */
  826.     (void)HTHostName();     /* Make address valid - doesn't work*/
  827.     sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
  828.             (int)*((unsigned char *)(&sin->sin_addr)+0),
  829.             (int)*((unsigned char *)(&sin->sin_addr)+1),
  830.             (int)*((unsigned char *)(&sin->sin_addr)+2),
  831.             (int)*((unsigned char *)(&sin->sin_addr)+3),
  832.             (int)*((unsigned char *)(&sin->sin_port)+0),
  833.             (int)*((unsigned char *)(&sin->sin_port)+1),
  834.             CR, LF);
  835.  
  836.  
  837. /*    Inform TCP that we will accept connections
  838. */
  839.     if (listen(master_socket, 1)<0) {
  840.     master_socket = -1;
  841.     return HTInetStatus("listen");
  842.     }
  843. #ifndef RELEASE
  844.     CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
  845. #endif /* RELEASE */
  846.     FD_SET(master_socket, &open_sockets);
  847.     if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
  848.  
  849.     return master_socket;        /* Good */
  850.  
  851. } /* get_listen_socket */
  852. #endif
  853.  
  854. typedef struct _EntryInfo {
  855.     char *       filename;
  856.     char *       type;
  857.     char *       date;
  858.     unsigned long int size;
  859.     BOOLEAN      display;  /* show this entry? */
  860. } EntryInfo;
  861.  
  862. PRIVATE void free_entryinfo_struct_contents ARGS1(EntryInfo *,entry_info)
  863. {
  864.     if(entry_info) {
  865.         if(entry_info->filename) free(entry_info->filename);
  866.         if(entry_info->type) free(entry_info->type);
  867.         if(entry_info->date) free(entry_info->date);
  868.     }
  869.    /* dont free the struct */
  870. }
  871.  
  872. /*
  873.  * is_ls_date() --
  874.  *      Return TRUE if s points to a string of the form:
  875.  *              "Sep  1  1990 " or
  876.  *              "Sep 11 11:59 " or
  877.  *              "Dec 12 1989  " or
  878.  *              "FCv 23 1990  " ...
  879.  */
  880. PRIVATE BOOLEAN is_ls_date ARGS1(char *,s)
  881. {
  882.         /* must start with three alpha characters */
  883.         if (!isalpha(*s++) || !isalpha(*s++) || !isalpha(*s++))
  884.                 return FALSE;
  885.  
  886.         /* space */
  887.         if (*s++ != ' ')
  888.                 return FALSE;
  889.  
  890.         /* space or digit */
  891.         if ((*s != ' ') && !isdigit(*s))
  892.                 return FALSE;
  893.         s++;
  894.  
  895.         /* digit */
  896.         if (!isdigit(*s++))
  897.                 return FALSE;
  898.  
  899.     /* space */
  900.         if (*s++ != ' ')
  901.                 return FALSE;
  902.  
  903.         /* space or digit */
  904.         if ((*s != ' ') && !isdigit(*s))
  905.                 return FALSE;
  906.         s++;
  907.  
  908.         /* digit */
  909.         if (!isdigit(*s++))
  910.                 return FALSE;
  911.  
  912.         /* colon or digit */
  913.         if ((*s != ':') && !isdigit(*s))
  914.                 return FALSE;
  915.         s++;
  916.  
  917.         /* digit */
  918.         if (!isdigit(*s++))
  919.                 return FALSE;
  920.  
  921.         /* space or digit */
  922.         if ((*s != ' ') && !isdigit(*s))
  923.                 return FALSE;
  924.         s++;
  925.  
  926.         /* space */
  927.         if (*s++ != ' ')
  928.                 return FALSE;
  929.  
  930.         return TRUE;
  931. } /* is_ls_date() */
  932.  
  933.  
  934. /*
  935.  * parse_ls_line() --
  936.  *      Extract the name, size, and date from an ls -l line.
  937.  */
  938. PRIVATE void parse_ls_line ARGS2(char *,line, EntryInfo *,entry_info)
  939. {
  940.         short   i, j;
  941.     auto unsigned long int    base=1L;
  942.     auto unsigned long int size_num = 0UL;
  943.  
  944.     for (i = strlen(line) - 1;
  945.         (i > 13) && (!isspace(line[i]) || !is_ls_date(&line[i-12])); i--)
  946.         ; /* null body */
  947.     line[i] = '\0';
  948.     if (i > 13) {
  949.         StrAllocCopy(entry_info->date, &line[i-12]);
  950.         /* replace the 4th location with nbsp if it is a space */
  951.         if(entry_info->date[4] == ' ')
  952.         entry_info->date[4] = HT_NON_BREAK_SPACE;
  953.     }
  954.     j = i - 14;
  955.     while (isdigit(line[j]))
  956.     {
  957.         size_num += (unsigned long)(line[j] - '0') * base;
  958.         base *= 10UL;
  959.         j--;
  960.     }
  961.     entry_info->size = size_num;
  962.     StrAllocCopy(entry_info->filename, &line[i + 1]);
  963. } /* parse_ls_line() */
  964.  
  965. /*
  966.  * parse_vms_dir_entry()
  967.  *      Format the name, date, and size from a VMS LIST line
  968.  *      into the EntryInfo structure
  969.  */
  970. PRIVATE void parse_vms_dir_entry ARGS2(char *,line, EntryInfo *,entry_info)
  971. {
  972.     int i, j;
  973.     long ialloc;
  974.     char *cp, *cpd, *cps, date[16], *sp = " ";
  975.     time_t NowTime;
  976.     static char ThisYear[8];
  977.     static BOOLEAN HaveYear = FALSE;
  978.  
  979.     /**  Get rid of blank lines, and information lines.  **/
  980.     /**  Valid lines have the ';' version number token.  **/
  981.     if (!strlen(line) || (cp=strchr(line, ';')) == NULL) {
  982.         entry_info->display = FALSE;
  983.         return;
  984.     }
  985.  
  986.         /** Cut out file or directory name at VMS version number. **/
  987.     *cp++ ='\0';
  988.     StrAllocCopy(entry_info->filename,line);
  989.  
  990.     /** Cast VMS file and directory names to lowercase. **/
  991.     for (i=0; entry_info->filename[i]; i++)
  992.         entry_info->filename[i] = tolower(entry_info->filename[i]);
  993.  
  994.         /** Uppercase terminal .z's or _z's. **/
  995.     if ((--i > 2) && entry_info->filename[i] == 'z' &&
  996.          (entry_info->filename[i-1] == '.' ||
  997.         entry_info->filename[i-1] == '_'))
  998.         entry_info->filename[i] = 'Z';
  999.  
  1000.         /** Convert any tabs in rest of line to spaces. **/
  1001.     cps = cp-1;
  1002.         while ((cps=strchr(cps+1, '\t')) != NULL)
  1003.             *cps = ' ';
  1004.  
  1005.         /** Collapse serial spaces. **/
  1006.     i = 0; j = 1;
  1007.     cps = cp;
  1008.         while (cps[j] != '\0') {
  1009.             if (cps[i] == ' ' && cps[j] == ' ')
  1010.                 j++;
  1011.             else
  1012.                 cps[++i] = cps[j++];
  1013.         }
  1014.         cps[++i] = '\0';
  1015.  
  1016.         /** Save the current year, if we don't have it yet.  It  **/
  1017.     /** could be wrong on New Year's Eve, if some poor soul  **/
  1018.     /** is using Lynx instead of kissing his/her sweetheart. **/
  1019.     if (!HaveYear) {
  1020.         NowTime = time(NULL);
  1021.          strcpy(ThisYear, (char *)ctime(&NowTime)+20);
  1022.         ThisYear[4] = '\0';
  1023.         HaveYear = TRUE;
  1024.     }
  1025.  
  1026.         /** Track down the date. **/
  1027.         if ((cpd=strchr(cp, '-')) != NULL &&
  1028.             strlen(cpd) > 9 && isdigit(*(cpd-1)) &&
  1029.         isalpha(*(cpd+1)) && *(cpd+4) == '-') {
  1030.  
  1031.         /** Month **/
  1032.             *(cpd+4) = '\0';
  1033.             *(cpd+2) = tolower(*(cpd+2));
  1034.             *(cpd+3) = tolower(*(cpd+3));
  1035.         sprintf(date, "%s ", cpd+1);
  1036.         *(cpd+4) = '-';
  1037.  
  1038.         /** Day **/
  1039.         *cpd = '\0';
  1040.         if (isdigit(*(cpd-2)))
  1041.             sprintf(date+4, "%s ", cpd-2);
  1042.         else
  1043.             sprintf(date+4, " %s ", cpd-1);
  1044.         *cpd = '-';
  1045.  
  1046.         /** Time or Year **/
  1047.         if (!strncmp(ThisYear, cpd+5, 4) &&
  1048.             strlen(cpd) > 15 && *(cpd+12) == ':') {
  1049.             *(cpd+15) = '\0';
  1050.             sprintf(date+7, "%s", cpd+10);
  1051.         *(cpd+15) = ' ';
  1052.         } else {
  1053.             *(cpd+9) = '\0';
  1054.             sprintf(date+7, " %s", cpd+5);
  1055.             *(cpd+9) = ' ';
  1056.         }
  1057.  
  1058.         StrAllocCopy(entry_info->date, date);
  1059.         }
  1060.  
  1061.     /** Track down the size **/
  1062.         if ((cpd=strchr(cp, '/')) != NULL) {
  1063.             /* Appears be in used/allocated format */
  1064.             cps = cpd;
  1065.             while (isdigit(*(cps-1)))
  1066.                 cps--;
  1067.             if (cps < cpd)
  1068.                 *cpd = '\0';
  1069.         entry_info->size = (unsigned long)atol(cps);
  1070.             cps = cpd+1;
  1071.             while (isdigit(*cps))
  1072.         cps++;
  1073.         *cps = '\0';
  1074.         ialloc = atol(cpd+1);
  1075.         /* Check if used is in blocks or bytes */
  1076.         if (entry_info->size <= ialloc)
  1077.         entry_info->size *= 512UL;
  1078.     }
  1079.     else if ((cps=strtok(cp, sp)) != NULL) {
  1080.         /* We just initialized on the version number */
  1081.         /* Now let's hunt for a lone, size number    */
  1082.         while ((cps=strtok(NULL, sp)) != NULL) {
  1083.            cpd = cps;
  1084.            while (isdigit(*cpd))
  1085.            cpd++;
  1086.            if (*cpd == '\0') {
  1087.            /* Assume it's blocks */
  1088.            entry_info->size = (unsigned long)atol(cps) * 512UL;
  1089.                    break;
  1090.                }
  1091.            }
  1092.         }
  1093.  
  1094.     /** Wrap it up **/
  1095. #ifndef RELEASE
  1096.     if (TRACE) fprintf(stderr,
  1097.              "HTFTP: VMS filename: %s  date: %s  size: %lu\n",
  1098.              entry_info->filename,
  1099.              entry_info->date ? entry_info->date : "",
  1100.              entry_info->size);
  1101. #endif /* RELEASE */
  1102.         return;
  1103. } /* parse_vms_dir_entry() */
  1104.  
  1105. /*
  1106.  *     parse_dir_entry() 
  1107.  *      Given a line of LIST/NLST output in entry, return results 
  1108.  *      and a file/dir name in entry_info struct
  1109.  *
  1110.  *      If first is true, this is the first name in a directory.
  1111.  */
  1112.  
  1113. PRIVATE EntryInfo * parse_dir_entry ARGS2(char *, entry, BOOLEAN *,first)
  1114. {
  1115.     EntryInfo *entry_info;
  1116.         int  i;
  1117.         int  len;
  1118.     BOOLEAN remove_size=FALSE;
  1119.  
  1120.     entry_info = (EntryInfo *)malloc(sizeof(EntryInfo));
  1121.     entry_info->type = 0;
  1122.     entry_info->size = 0UL;
  1123.     entry_info->date = 0;
  1124.     entry_info->filename = 0;
  1125.     entry_info->display = TRUE;
  1126.  
  1127.     switch (server_type)
  1128.         {
  1129.         case UNIX_SERVER:
  1130.         case PETER_LEWIS_SERVER:
  1131.         case MACHTEN_SERVER:
  1132.  
  1133.                 /* interpret and edit LIST output from Unix server */
  1134.                len = strlen(entry);
  1135.         
  1136.  
  1137.            if (*first) {
  1138.  
  1139.            *first=FALSE;
  1140.                    if(!strncmp(entry, "total ", 6) ||
  1141.                                (strstr(entry, "not available") != NULL))
  1142.              {
  1143.             entry_info->display=FALSE;
  1144.             return(entry_info);
  1145.              }
  1146.             else if(unsure_type)
  1147.               {
  1148.                          /* this isn't really a unix server! */
  1149.                          server_type = GENERIC_SERVER;
  1150.              entry_info->display=FALSE;
  1151.              return(entry_info);
  1152.               }
  1153.            }
  1154.  
  1155.                /* check first character of ls -l output */
  1156.                if (toupper(entry[0]) == 'D') 
  1157.          {
  1158.                    /* it's a directory */
  1159.            StrAllocCopy(entry_info->type, "Directory");
  1160.            remove_size=TRUE; /* size is not useful */
  1161.          }
  1162.                else if (entry[0] == 'l')
  1163.          {
  1164.                     /* it's a symbolic link, does the user care about
  1165.              * knowing if it is symbolic?  I think so since
  1166.              * it might be a directory
  1167.              */
  1168.             StrAllocCopy(entry_info->type, "Symbolic Link");
  1169.             remove_size=TRUE; /* size is not useful */
  1170.  
  1171.                     /* strip off " -> pathname" */
  1172.             for (i = len - 1; (i > 3) && (!isspace(entry[i])
  1173.                     || (entry[i-1] != '>') 
  1174.                     || (entry[i-2] != '-')
  1175.                     || (entry[i-3] != ' ')); i--)
  1176.                              ; /* null body */
  1177.                     if (i > 3)
  1178.                       {
  1179.                         entry[i-3] = '\0';
  1180.                         len = i - 3;
  1181.                       }
  1182.                   } /* link */
  1183.  
  1184.         parse_ls_line(entry, entry_info);
  1185.  
  1186.         if(!strcmp(entry_info->filename,"..") ||
  1187.                     !strcmp(entry_info->filename,"."))
  1188.             entry_info->display=FALSE;
  1189.         
  1190.         /* goto the bottom and get real type */
  1191.                 break;
  1192.  
  1193.         case VMS_SERVER:
  1194.                 /* Interpret and edit LIST output from VMS server */
  1195.         /* and convert information lines to zero length.  */
  1196.         parse_vms_dir_entry(entry, entry_info);
  1197.  
  1198.                 /* Get rid of any junk lines */
  1199.         if(!entry_info->display)
  1200.             return(entry_info);
  1201.  
  1202.         /** Trim off VMS directory extensions **/
  1203.         len = strlen(entry_info->filename);
  1204.         if ((len > 4) && !strcmp(&entry_info->filename[len-4], ".dir"))
  1205.           {
  1206.             entry_info->filename[len-4] = '\0';
  1207.             StrAllocCopy(entry_info->type, "Directory");
  1208.             remove_size=TRUE; /* size is not useful */
  1209.           }
  1210.         /* goto the bottom and get real type */
  1211.                 break;
  1212.  
  1213.         case CMS_SERVER:
  1214.         /* can't be directory... */
  1215.         /*
  1216.          * "entry" already equals the correct filename
  1217.          */
  1218.         StrAllocCopy(entry_info->filename,entry);
  1219.         /* goto the bottom and get real type */
  1220.                 break;
  1221.  
  1222.         case NCSA_SERVER:
  1223.         case TCPC_SERVER:
  1224.                 /* directories identified by trailing "/" characters */
  1225.         StrAllocCopy(entry_info->filename,entry);
  1226.                 len = strlen(entry);
  1227.                 if (entry[len-1] == '/')
  1228.                 {
  1229.                         /* it's a dir, remove / and mark it as such */
  1230.                         entry[len-1] = '\0';
  1231.             StrAllocCopy(entry_info->type, "Directory");
  1232.             remove_size=TRUE; /* size is not useful */
  1233.                 }
  1234.         /* goto the bottom and get real type */
  1235.                 break;
  1236.     
  1237.     default:
  1238.         /* we cant tell if it is a directory since we only
  1239.          * did an NLST :(  List bad file types anyways? NOT!
  1240.          */
  1241.         StrAllocCopy(entry_info->filename,entry);
  1242.         return(entry_info); /* mostly empty info */
  1243.  
  1244.         } /* switch (server_type) */
  1245.  
  1246.  
  1247.     if(remove_size && entry_info->size)
  1248.       {
  1249.         entry_info->size = 0UL;
  1250.       }
  1251.  
  1252.     /* get real types eventually */
  1253.     if(!entry_info->type) {
  1254.         char *cp;
  1255.             HTFormat format;
  1256.             HTAtom * encoding;  /* @@ not used at all */
  1257.         format = HTFileFormat(entry_info->filename, &encoding);
  1258.  
  1259.         if(!strncmp(HTAtom_name(format), "application",11)) 
  1260.           {
  1261.            cp = HTAtom_name(format) + 12;
  1262.            if(!strncmp(cp,"x-",2))
  1263.             cp+=2;
  1264.           }
  1265.         else
  1266.         cp = HTAtom_name(format);
  1267.  
  1268.         StrAllocCopy(entry_info->type, cp);
  1269.     }
  1270.  
  1271.     return(entry_info);
  1272.  
  1273. } /* parse_dir_entry */
  1274.  
  1275. PUBLIC int compare_EntryInfo_structs ARGS2(EntryInfo *,entry1, 
  1276.                             EntryInfo *,entry2)
  1277. {
  1278.     int status;
  1279.  
  1280.     switch(HTfileSortMethod)
  1281.       {
  1282.         case FILE_BY_SIZE:
  1283.             /* both equal or both 0 */
  1284.             if(entry1->size == entry2->size)
  1285.                 return(strcasecomp(entry1->filename, 
  1286.                             entry2->filename));
  1287.             else
  1288.                 if(entry1->size > entry2->size)
  1289.                 return(1);
  1290.                 else
  1291.                 return(-1);
  1292.         case FILE_BY_TYPE:
  1293.                         if(entry1->type && entry2->type) {
  1294.                             status = strcasecomp(entry1->type, entry2->type);
  1295.                 if(status)
  1296.                 return(status);
  1297.                 /* else fall to filename comparison */
  1298.             }
  1299.                         return (strcasecomp(entry1->filename, 
  1300.                             entry2->filename));
  1301.         case FILE_BY_DATE:
  1302.                         if(entry1->date && entry2->date) {
  1303.                                 /* We really should change the type :( */
  1304.                             status = strcasecomp(entry1->date, entry2->date);
  1305.                 if(status)
  1306.                 return(status);
  1307.                 /* else fall to filename comparison */
  1308.             }
  1309.                         return (strcasecomp(entry1->filename, 
  1310.                             entry2->filename));
  1311.         case FILE_BY_NAME:
  1312.         default:
  1313.                         return (strcasecomp(entry1->filename, 
  1314.                             entry2->filename));
  1315.       }
  1316. }
  1317.  
  1318.  
  1319. /*    Read a directory into an hypertext object from the data socket
  1320. **    --------------------------------------------------------------
  1321. **
  1322. ** On entry,
  1323. **    anchor        Parent anchor to link the this node to
  1324. **    address        Address of the directory
  1325. ** On exit,
  1326. **    returns        HT_LOADED if OK
  1327. **            <0 if error.
  1328. */
  1329. PRIVATE int read_directory
  1330. ARGS4 (
  1331.   HTParentAnchor *,        parent,
  1332.   CONST char *,            address,
  1333.   HTFormat,            format_out,
  1334.   HTStream *,            sink )
  1335. {
  1336.     int status;
  1337.     BOOLEAN WasInterrupted = FALSE;
  1338.     HTStructured* target = HTML_new(parent, format_out, sink);
  1339.     HTStructuredClass targetClass;
  1340.     char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
  1341.     EntryInfo *entry_info;
  1342.     BOOLEAN first=TRUE;
  1343.     char string_buffer[64];
  1344.  
  1345.     char c = 0;
  1346.  
  1347.     char *lastpath=0;  /* prefix for link, either "" (for root) or xxx  */
  1348.     char *entry;   /* pointer into lastpath to bit after last slash */
  1349.  
  1350.     targetClass = *(target->isa);
  1351.  
  1352.     HTDirTitles(target, (HTAnchor*)parent);
  1353.   
  1354.     data_read_pointer = data_write_pointer = data_buffer;
  1355.  
  1356.     if (*filename == '\0')  /* Empty filename : use root */
  1357.         StrAllocCopy (lastpath, "/");
  1358.     else if(!strcmp(filename,"/"))  /* root path */
  1359.         StrAllocCopy (lastpath, "/foo/..");
  1360.     else 
  1361.     {
  1362.         char * p = strrchr(filename, '/');  /* find lastslash */
  1363.         StrAllocCopy(lastpath, p+1);    /* take slash off the beginning */
  1364.     }
  1365.     free (filename);
  1366.  
  1367.    
  1368.     {
  1369.         HTBTree * bt = HTBTree_new((HTComparer)compare_EntryInfo_structs);
  1370.         char c;
  1371.     HTChunk * chunk = HTChunkCreate(128);
  1372.     int BytesReceived = 0;
  1373.     int BytesReported = 0;
  1374.     char NumBytes[20];
  1375.     PUTS("\n");  /* prettier LJM */
  1376.     for (c=0; c!=(char)EOF;)   /* For each entry in the directory */
  1377.     {
  1378.         HTChunkClear(chunk);
  1379.  
  1380.         /*   read directory entry
  1381.          */
  1382.         for(;;) {                 /* Read in one line as filename */
  1383.         c = NEXT_DATA_CHAR;
  1384. AgainForMultiNet:
  1385.         if (c == '\r' || c == LF) {    /* Terminator? */
  1386.             if (chunk->size != 0) {  /* got some text */
  1387.                 /* Deal with MultiNet's wrapping of long lines */
  1388.                         if (server_type == VMS_SERVER) {
  1389.                         /* Deal with MultiNet's wrapping of long lines - F.M. */
  1390.                             if (data_read_pointer < data_write_pointer &&
  1391.                                 *(data_read_pointer+1) == ' ')
  1392.                                 data_read_pointer++;
  1393.                             else if (data_read_pointer >= data_write_pointer) {
  1394.                                 int status;
  1395.                 status = NETREAD(data_soc, data_buffer,
  1396.                          DATA_BUFFER_SIZE);
  1397.                 if(status <= 0)    {
  1398.                                     c = (char)EOF;
  1399.                                     break;
  1400.                                 }
  1401.                                 data_write_pointer = data_buffer + status;
  1402.                                 data_read_pointer = data_buffer;
  1403.                                 if (*data_read_pointer == ' ')
  1404.                                     data_read_pointer++;
  1405.                                 else
  1406.                                     break;
  1407.                             }
  1408.                 else
  1409.                                 break;
  1410.                         }
  1411.             else
  1412.                     break;            /* finish getting one entry */
  1413.             }
  1414.         } else if (c == (char)EOF) {
  1415.             break;             /* End of file */
  1416.         } else {
  1417.             HTChunkPutc(chunk, c);
  1418.         }
  1419.             }
  1420.         HTChunkTerminate(chunk);
  1421.  
  1422.         BytesReceived += chunk->size;
  1423.         if (BytesReceived > BytesReported + 1024) {
  1424.         BytesReported = BytesReceived;
  1425.         }
  1426.  
  1427.         if (c == (char) EOF && chunk->size == 1)
  1428.         /* 1 means empty: includes terminating 0 */
  1429.             break;
  1430. #ifndef RELEASE
  1431.         if(TRACE) fprintf(stderr, "HTFTP: Line in %s is %s\n",
  1432.                         lastpath, chunk->data);
  1433. #endif /* RELEASE */
  1434.  
  1435.         entry_info = parse_dir_entry(chunk->data, &first);
  1436.         if(entry_info->display)
  1437.           {
  1438. #ifndef RELEASE
  1439.          if(TRACE)
  1440.             fprintf(stderr,"Adding file to BTree: %s\n",
  1441.                         entry_info->filename);
  1442. #endif /* RELEASE */
  1443.          HTBTree_add(bt, (EntryInfo *)entry_info);
  1444.           }
  1445.  
  1446.     }  /* next entry */
  1447.  
  1448. unload_btree:
  1449.  
  1450.         HTChunkFree(chunk);
  1451.  
  1452.     /* print out the handy help message if it exits :) */
  1453.     if(server_type == UNIX_SERVER && help_message_cache_non_empty()) {
  1454.         START(HTML_HR);
  1455.         START(HTML_PRE);
  1456.         PUTS(help_message_cache_contents());
  1457.         init_help_message_cache();  /* to free memory */
  1458.         START(HTML_HR);
  1459.     } else {
  1460.         START(HTML_PRE);
  1461.         PUTS("\n");
  1462.     }
  1463.  
  1464.        
  1465.     /* Run through tree printing out in order 
  1466.      */
  1467.     {
  1468.         HTBTElement * ele;
  1469.         int i;
  1470.         for (ele = HTBTree_next(bt, NULL);
  1471.          ele != NULL;
  1472.          ele = HTBTree_next(bt, ele))
  1473.         {
  1474.         entry_info = (EntryInfo *)HTBTree_object(ele);
  1475.  
  1476.         if(entry_info->date)
  1477.                {
  1478.                      PUTS("[");
  1479.                  PUTS(entry_info->date);
  1480.                      PUTS("]  ");
  1481.                }
  1482.         else
  1483.             PUTS("     * ");
  1484.  
  1485.         if(entry_info->type)
  1486.               {
  1487.             for(i = 0; entry_info->type[i] != '\0' && i < 15; i++)
  1488.                 PUTC(entry_info->type[i]);
  1489.                 for(; i < 17; i++)
  1490.                     PUTC(' ');
  1491.               }
  1492.  
  1493.         /* start the anchor */
  1494.         HTDirEntry(target, lastpath, entry_info->filename);
  1495.         PUTS(entry_info->filename);
  1496.         END(HTML_A);
  1497.  
  1498.         if(entry_info->size)
  1499.                {
  1500.                 sprintf(string_buffer,"  (%lu Bytes)",
  1501.                             entry_info->size);
  1502.                  PUTS(string_buffer);
  1503.                }
  1504.  
  1505.         PUTC('\n');
  1506.         free_entryinfo_struct_contents(entry_info);
  1507.         }
  1508.     }
  1509.     FREE_TARGET;
  1510.     HTBTreeAndObject_free(bt);
  1511.     }
  1512.  
  1513.     if (lastpath) free(lastpath);
  1514.     return response(NIL) == 2 ? HT_LOADED : -1;
  1515. }
  1516.  
  1517. /*    Retrieve File from Server
  1518. **    -------------------------
  1519. **
  1520. ** On entry,
  1521. **    name        WWW address of a file: document, including hostname
  1522. ** On exit,
  1523. **    returns        Socket number for file if good.
  1524. **            <0 if bad.
  1525. */
  1526. PUBLIC int HTFTPLoad
  1527. ARGS4 (
  1528.   CONST char *,            name,
  1529.   HTParentAnchor *,        anchor,
  1530.   HTFormat,            format_out,
  1531.   HTStream *,            sink
  1532. )
  1533. {
  1534.     BOOL isDirectory = NO;
  1535.     int status;
  1536.     int retry;            /* How many times tried? */
  1537.     HTFormat format;
  1538.     char command[LINE_LENGTH+1];
  1539.     
  1540.  
  1541.     /* set use_list to NOT since we don't know what kind of server
  1542.      * this is yet.  And set the type to GENERIC
  1543.      */
  1544.     use_list = FALSE;
  1545.     server_type = GENERIC_SERVER;
  1546.  
  1547.     for (retry=0; retry<2; retry++) {    /* For timed out/broken connections */
  1548.     
  1549.     status = get_connection(name);
  1550.     if (status<0) return status;
  1551.  
  1552. #ifdef LISTEN
  1553.     status = get_listen_socket();
  1554.     if (status<0) {
  1555.         NETCLOSE (control->socket);
  1556.             control->socket = -1;
  1557.             close_master_socket ();
  1558.         /* HT_INTERRUPTED would fall through, if we could interrupt
  1559.                somehow in the middle of it, which we currently can't. */
  1560.         return status;
  1561.     }
  1562.     
  1563. #ifdef REPEAT_PORT
  1564. /*    Inform the server of the port number we will listen on
  1565. */
  1566.     {
  1567.         status = response(port_command);
  1568.         if (status == HT_INTERRUPTED) {
  1569. #ifndef RELEASE
  1570.           if (TRACE)
  1571.         fprintf (stderr, "FTP: Interrupted in response (port_command)\n");
  1572. #endif /* RELEASE */
  1573.           HTProgress ("Connection interrupted.");
  1574.               NETCLOSE (control->socket);
  1575.               control->socket = -1;
  1576.               close_master_socket ();
  1577.           return HT_INTERRUPTED;
  1578.             }
  1579.         if (status !=2) {        /* Could have timed out */
  1580.         if (status<0) continue;        /* try again - net error*/
  1581.         return -status;            /* bad reply */
  1582.         }
  1583. #ifndef RELEASE
  1584.         if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
  1585. #endif /* RELEASE */
  1586.     }
  1587. #endif
  1588. #else    /* Use PASV */
  1589. /*    Tell the server to be passive
  1590. */
  1591.     {
  1592.         char *p;
  1593.         int reply, h0, h1, h2, h3, p0, p1;    /* Parts of reply */
  1594.         int status;
  1595.         data_soc = status;
  1596.  
  1597.         sprintf(command, "PASV%c%c", CR, LF);
  1598.         status = response(command);
  1599.         if (status !=2) {
  1600.         if (status<0) continue;        /* retry or Bad return */
  1601.         return -status;            /* bad reply */
  1602.         }
  1603.         for(p=response_text; *p && *p != ','; p++)
  1604.         ; /* null body */
  1605.  
  1606.         while (--p > response_text && '0' <= *p && *p <= '9')
  1607.         ; /* null body */
  1608.  
  1609.        status = sscanf(p+1, "%d,%d,%d,%d,%d,%d",
  1610.            &h0, &h1, &h2, &h3, &p0, &p1);
  1611.        if (status<4) {
  1612.            fprintf(stderr, "FTP: PASV reply has no inet address!\n");
  1613.            return -99;
  1614.        }
  1615.        passive_port = (p0<<8) + p1;
  1616. #ifndef RELEASE
  1617.        if(TRACE)
  1618.            fprintf(stderr, "FTP: Server is listening on port %d\n",
  1619.             passive_port);
  1620. #endif /* RELEASE */
  1621.  
  1622.  
  1623. /*    Open connection for data:
  1624. */
  1625.         sprintf(command,
  1626.         "ftp://%d.%d.%d.%d:%d/",h0,h1,h2,h3,passive_port);
  1627.  
  1628.         status = HTDoConnect(name, "FTP", passive_port, &data_soc);
  1629.  
  1630.  
  1631.         if (status<0){
  1632.         (void) HTInetStatus("connect for data");
  1633.         NETCLOSE(data_soc);
  1634.         return status;            /* Bad return */
  1635.         }
  1636.  
  1637. #ifndef RELEASE
  1638.         if (TRACE) fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
  1639. #endif /* RELEASE */
  1640.     }
  1641. #endif /* use PASV */
  1642.     status = 0;
  1643.     break;    /* No more retries */
  1644.  
  1645.     } /* for retries */
  1646.     if (status<0) return status;    /* Failed with this code */
  1647.  
  1648. /*    Ask for the file:
  1649. */
  1650.     {
  1651.     char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
  1652.     char *fname = filename;    /** Save for subsequent free() **/
  1653.     BOOL binary;
  1654.     HTAtom * encoding;
  1655.  
  1656.     if (!*filename) StrAllocCopy(filename, "/");
  1657.     HTUnEscape(filename);
  1658. #ifndef RELEASE
  1659.     if (TRACE) fprintf(stderr, "FTP: UnEscaped %d\n", filename);
  1660. #endif /* RELEASE */
  1661.     format = HTFileFormat(filename, &encoding);
  1662.     binary = (encoding != HTAtom_for("8bit")
  1663.           && encoding != HTAtom_for("7bit"));
  1664.     if (binary != control->binary) {
  1665.         char * mode = binary ? "I" : "A";
  1666.         sprintf(command, "TYPE %s%c%c", mode, CR, LF);
  1667.         status = response(command);
  1668.         if (status != 2) return -status;
  1669.         control->binary = binary;
  1670.     }
  1671.     if (server_type == VMS_SERVER) {
  1672.         char *cp, *cp1, *cp2;
  1673.         /** Accept only Unix-style filename **/
  1674.         if (strchr(filename, ':') != NULL ||
  1675.         strchr(filename, '[') != NULL) {
  1676.         free(fname);
  1677.         return -1;
  1678.         }
  1679.         /** Trim trailing slash if filename is not the top directory **/
  1680.         if (strlen(filename) > 1 && filename[strlen(filename)-1] == '/')
  1681.             filename[strlen(filename)-1] = '\0';
  1682.  
  1683. #ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */
  1684.         /** Get the current default VMS device:[directory] **/
  1685.         sprintf(command, "PWD%c%c", CR, LF);
  1686.         status = response (command);
  1687.         if (status != 2) {
  1688.              free(fname);
  1689.          return -status;
  1690.         }
  1691.         /** Go to the VMS account's top directory **/
  1692.         if ((cp=strchr(response_text, '[')) != NULL &&
  1693.             (cp1=strrchr(response_text, ']')) != NULL) {
  1694.         sprintf(command, "CWD %s", cp);
  1695.         if ((cp2=strchr(cp, '.')) != NULL && cp2 < cp1)
  1696.             sprintf(command+(cp2-cp)+4, "]%c%c", CR, LF);
  1697.         else
  1698.             sprintf(command+(cp1-cp)+4, "]%c%c", CR, LF);
  1699.         status = response (command);
  1700.         if (status != 2) {
  1701.             free(fname);
  1702.             return -status;
  1703.         }
  1704.         }
  1705. #endif /* MAINTAIN_CONNECTION */
  1706.  
  1707.         /** If we want the VMS account's top directory, list it now **/
  1708.         if (strlen(filename) == 1 && *filename == '/') {
  1709.         isDirectory = YES;
  1710.         sprintf(command, "LIST%c%c", CR, LF);
  1711.         status = response (command);
  1712.         free(fname);
  1713.         if (status != 1) return -status;  /* Action not started */
  1714.  
  1715.         goto listen;  /* big goto */
  1716.         }
  1717.         /** Otherwise, go to appropriate directory and doctor filename **/
  1718.         if ((cp=strchr(filename, '/')) != NULL &&
  1719.             (cp1=strrchr(cp, '/')) != NULL && cp != cp1) {
  1720.         sprintf(command, "CWD [.%s", cp+1);
  1721.         sprintf(command+(cp1-cp)+5, "]%c%c", CR, LF);
  1722.         while ((cp2=strrchr(command, '/')) != NULL)
  1723.             *cp2 = '.';
  1724.         status = response(command);
  1725.         if (status != 2) {
  1726.             free(fname);
  1727.             return -status;
  1728.         }
  1729.         filename = cp1+1;
  1730.         }
  1731.         else
  1732.             filename += 1;
  1733.     }
  1734.     sprintf(command, "RETR %s%c%c", filename, CR, LF);
  1735.     status = response(command);
  1736.     if (status != 1) {  /* Failed : try to CWD to it */
  1737.  
  1738.        /* save those handy help messages */
  1739.       if(server_type == UNIX_SERVER)
  1740.           init_help_message_cache();
  1741.  
  1742.       sprintf(command, "CWD %s%c%c", filename, CR, LF);
  1743.       status = response(command);
  1744.  
  1745.       if (status == 2) {  /* Successed : let's NAME LIST it */
  1746.         isDirectory = YES;
  1747.         if(use_list)
  1748.             sprintf(command, "LIST%c%c", CR, LF);
  1749.         else
  1750.             sprintf(command, "NLST%c%c", CR, LF);
  1751.         status = response (command);
  1752.       }
  1753.     }
  1754.     free(fname);
  1755.     if (status != 1) return -status;        /* Action not started */
  1756.     }
  1757.  
  1758. listen:
  1759. #ifdef LISTEN
  1760. /*    Wait for the connection
  1761. */
  1762.     {
  1763.     struct sockaddr_in soc_address;
  1764.     int    soc_addrlen=sizeof(soc_address);
  1765.     status = accept(master_socket,
  1766.             (struct sockaddr *)&soc_address,
  1767.             &soc_addrlen);
  1768.     if (status<0)
  1769.         return HTInetStatus("accept");
  1770. #ifndef RELEASE
  1771.     CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
  1772. #endif /* RELEASE */
  1773.     data_soc = status;
  1774.     }
  1775. #else
  1776. /* @@ */
  1777. #endif
  1778.     if (isDirectory) {
  1779.         status = read_directory (anchor, name, format_out, sink);
  1780.         NETCLOSE(data_soc);
  1781.     NETCLOSE(control->socket);
  1782.         return status;
  1783.       /* returns HT_LOADED or error */
  1784.     } else {
  1785.         int rv;
  1786.  
  1787.     rv = HTParseSocket(format, format_out, anchor, data_soc, sink);
  1788.  
  1789.     HTInitInput(control->socket);
  1790.     /* Reset buffering to control connection DD 921208 */
  1791.     
  1792. #ifndef RELEASE
  1793.     if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
  1794. #endif /* RELEASE */
  1795.     status = NETCLOSE(data_soc);
  1796.     if (status<0 && rv != -1)
  1797.         (void) HTInetStatus("close");    /* Comment only */
  1798.     data_soc = -1;    /* invalidate it */
  1799.  
  1800. #ifndef MSDOS
  1801.     /*
  1802.      *    Skip this step in MSDOS.
  1803.      *    Only causes a delay.
  1804.      */
  1805.     status = response(NIL);        /* Pick up final reply */
  1806.     if (status != 2 && rv != -1)
  1807.         return HTLoadError(sink, 500, response_text);
  1808. #endif /* MSDOS */
  1809.  
  1810.     NETCLOSE(control->socket);
  1811.     return HT_LOADED;
  1812.     }       
  1813. } /* open_file_read */
  1814.